package org.hsieh.tr.explore.ui;

import java.sql.Connection;
import java.sql.Driver;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.hsieh.tr.explore.db.spi.IDriverListener;
import org.hsieh.tr.explore.dbconn.ConnectionFactory;
import org.hsieh.tr.explore.dbsource.IDatabaseInfo;
import org.hsieh.tr.explore.dbsource.impl.DatabaseInfoImpl;
import org.hsieh.tr.explore.util.JFaceUtil;
import org.hsieh.tr.explore.util.StringUtil;

/**
 * 
 * 
 *@author hsieh 234aini@gmail.com
 * 
 */
public class NewConnectionWizardPage extends WizardPage implements
	IDriverListener {

    private ClasspathTableComposite classpathEditor;
    private Text hostText;
    private Text portText;
    private Text userNameText;
    private Text passwordText;
    private Text driverClass;
    private Text dbNameText;

    private Text connectNameText;

    private IDatabaseInfo info = null;

    protected NewConnectionWizardPage(String pageName) {
	super(pageName);
	setTitle("Create connection");
    }

    protected NewConnectionWizardPage(String pageName, IDatabaseInfo existInfo) {
	this(pageName);
	this.info = existInfo;
    }

    public void createControl(Composite parent) {
	GridLayoutFactory.swtDefaults().numColumns(2).applyTo(parent);
	connectNameText = createLabelText(parent, "Connection Name");

	hostText = createLabelText(parent, "Host");
	portText = createLabelText(parent, "Port");
	userNameText = createLabelText(parent, "User name");
	passwordText = createLabelText(parent, "Password");

	Label label = new Label(parent, SWT.NONE);
	label.setText("Classpath");
	classpathEditor = new ClasspathTableComposite(parent, SWT.NONE);
	classpathEditor.setLayoutData(new GridData(GridData.FILL_BOTH));

	classpathEditor.addDriverListener(this);

	driverClass = createLabelText(parent, "Driver");

	dbNameText = createLabelText(parent, "Database");

	createTestConnectionButton(parent);

	setControl(hostText);

	if (info != null) {
	    syncInfoToPage();
	}
	validate();
    }

    private void syncInfoToPage() {

	setTextValue(connectNameText, info.getName());
	setTextValue(hostText, info.getConnectUrl());
	setTextValue(driverClass, info.getDriverClassName());
	setTextValue(portText, info.getPort());
	setTextValue(userNameText, info.getUserName());
	setTextValue(passwordText, info.getPassword());
	setTextValue(dbNameText, info.getDbName());

	if (info.getDriverClassPath() != null
		&& !info.getDriverClassPath().isEmpty()) {
	    classpathEditor.addClassPathes(info.getDriverClassPath());
	    classpathEditor.refreshList();
	}

    }

    private void setTextValue(Text text, String value) {
	if (!StringUtil.isNullOrEmpty(value))
	    text.setText(value);
    }

    private void createTestConnectionButton(Composite parent) {
	final Button button = new Button(parent, SWT.NONE);
	button.setText("Test connection");
	GridData bd = new GridData();
	bd.horizontalSpan = 2;
	bd.horizontalAlignment = SWT.CENTER;

	button.setLayoutData(bd);

	button.addSelectionListener(new SelectionListener() {

	    public void widgetDefaultSelected(SelectionEvent e) {
		widgetSelected(e);
	    }

	    public void widgetSelected(SelectionEvent e) {
		final IDatabaseInfo info = composeInfo();

		BusyIndicator.showWhile(getControl().getDisplay(),
			new Runnable() {

			    public void run() {
				try {
				    Connection connect = ConnectionFactory
					    .connect(info);
				    if (connect == null) {
					MessageDialog
						.openError(button.getShell(),
							"Connection failed.",
							"Failed. Please make sure your host is true");
				    } else {
					connect.close();
					MessageDialog.openInformation(button
						.getShell(), "Connection OK",
						"OK");
				    }
				} catch (Exception e1) {
				    MessageDialog.openError(button.getShell(),
					    "Connect error", e1.toString());
				}
			    }

			});

	    }

	});
    }

    private IDatabaseInfo composeInfo() {
	if (this.info == null) {
	    info = new DatabaseInfoImpl();
	}
	info.setName(getContent(connectNameText));

	info.setConnectUrl(getContent(hostText));
	info.setDriverClassName(getContent(driverClass));
	info.setPort(getContent(portText));

	info.setUserName(getContent(userNameText));
	info.setPassword(getContent(passwordText));
	info.setDriverClassPath(new ArrayList<String>(classpathEditor
		.getClasspathes()));

	info.setDbName(getContent(dbNameText));
	return info;
    }

    private String getContent(Text text) {
	return text.getText();
    }

    private Text createLabelText(Composite parent, String labelText) {
	Label label = new Label(parent, SWT.NONE);
	label.setText(labelText);
	Text text = new Text(parent, SWT.BORDER);
	GridData data = new GridData(GridData.FILL_HORIZONTAL);
	GridLayout layout = (GridLayout) parent.getLayout();
	data.horizontalSpan = layout.numColumns - 1;
	text.setLayoutData(data);

	text.addModifyListener(new ModifyListener() {

	    public void modifyText(ModifyEvent e) {
		validate();
	    }
	});

	return text;
    }

    public IDatabaseInfo getDatabaseInfo() {
	return composeInfo();
    }

    public <T extends Driver> void driversAreFound(List<Class<T>> drivers) {
	if (drivers.size() == 1) {
	    // set class name
	    updateDriverClassName(drivers.get(0).getCanonicalName());
	} else {
	    // open a dialog to select
	    DriverDialog dialog = new DriverDialog(getShell(), drivers);
	    if (dialog.open() == Dialog.OK) {
		String className = dialog.getClassName();
		if (className != null)
		    updateDriverClassName(className);
	    }
	}

    }

    private void updateDriverClassName(final String klass) {
	driverClass.setText(klass);
    }

    private void validate() {
	if (validateTextField(connectNameText, "Connection name is empty")
		&& validateTextField(hostText, "Host is empty")
		&& validateTextField(userNameText, "User name is empty")
		&& validateTextField(driverClass, "Driver class is empty")) {
	    setErrorMessage(null);
	    setPageComplete(true);
	}
    }

    private boolean validateTextField(final Text text, String errorMessage) {
	if (StringUtil.isNullOrEmpty(text.getText())) {
	    // text.setFocus();
	    setErrorMessage(errorMessage);
	    setPageComplete(false);
	    return false;
	} else {
	    return true;
	}
    }

    class DriverDialog extends Dialog {

	private final Object input;
	private ListViewer list;
	private String className;

	protected DriverDialog(Shell parentShell, Object input) {
	    super(parentShell);
	    this.input = input;
	}

	@Override
	protected Control createDialogArea(Composite parent) {
	    Composite composite = (Composite) super.createDialogArea(parent);
	    GridLayoutFactory.swtDefaults().margins(0, 0).applyTo(composite);

	    Label label = new Label(composite, SWT.NONE);
	    label.setText("Please choose a Driver:");

	    list = new ListViewer(composite, SWT.SINGLE | SWT.BORDER);
	    GridDataFactory.fillDefaults().grab(true, true).applyTo(
		    list.getList());

	    list.setContentProvider(new IStructuredContentProvider() {

		public void dispose() {

		}

		public void inputChanged(Viewer viewer, Object oldInput,
			Object newInput) {

		}

		@SuppressWarnings("unchecked")
		public Object[] getElements(Object inputElement) {
		    if (inputElement instanceof List) {
			return ((List) inputElement).toArray(new Object[0]);
		    }
		    return null;
		}
	    });

	    list.setLabelProvider(new ILabelProvider() {

		public Image getImage(Object element) {
		    return null;
		}

		public String getText(Object element) {
		    if (element instanceof Class<?>) {
			return ((Class<?>) element).getCanonicalName();
		    } else
			return null;
		}

		public void addListener(ILabelProviderListener listener) {

		}

		public void dispose() {

		}

		public boolean isLabelProperty(Object element, String property) {
		    return false;
		}

		public void removeListener(ILabelProviderListener listener) {

		}

	    });

	    list.setInput(input);

	    list.addSelectionChangedListener(new ISelectionChangedListener() {

		public void selectionChanged(SelectionChangedEvent event) {
		    Object driver = JFaceUtil.getFristSelected(event
			    .getSelection());
		    if (driver != null) {
			setClassName(((Class) driver).getCanonicalName());
		    }
		}
	    });
	    return composite;
	}

	public String getClassName() {
	    return className;
	}

	public void setClassName(String className) {
	    this.className = className;
	}

	@Override
	protected void configureShell(Shell newShell) {
	    super.configureShell(newShell);
	    newShell.setSize(new Point(520, 300));
	    newShell.setText("Driver choose");
	}

	@Override
	protected boolean isResizable() {
	    return true;
	}

    }

}
